@using System.Text.RegularExpressions;
@using System.IO;
@using System.Net.Http;
@using System.Reflection;
@using Microsoft.Extensions.DependencyInjection;
@using System.Linq;
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@inject IJSRuntime JSRuntime
@inject HttpClient Http
@inject NavigationManager NavigationManager
@inject IServiceProvider ServiceProvider

<RadzenRow class="rz-p-2" Gap="0.5rem" Style="border-bottom: var(--rz-border-disabled);">
    <RadzenColumn>
        <RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" JustifyContent="JustifyContent.Start" Gap="0.5rem">
            <RadzenButton Visible=@(!ReadOnly) IsBusy=@isBusy Disabled=@isBusy Click="Run" Text="Run" Icon="play_circle" ButtonStyle="ButtonStyle.Base" Size="ButtonSize.ExtraSmall" Variant="Variant.Text" Title="Preview edits" class="rz-text-truncate" />
            <button class="rz-button rz-button-xs rz-variant-text rz-base rz-shade-default rz-text-truncate" Title="Copy source code" onclick=@Copy >
                <RadzenIcon Icon="content_copy" />
                Copy
            </button>
            <RadzenButton Visible=@(!ReadOnly) Disabled=@isBusy Click="@Reset" Text="Reset" Icon="refresh" ButtonStyle="ButtonStyle.Base" Size="ButtonSize.ExtraSmall" Variant="Variant.Text" class="rz-text-truncate" />
        </RadzenStack>
    </RadzenColumn>
    <RadzenColumn>
    @if (String.IsNullOrEmpty(ComponentName))
    {

    }
    else
    {
        <RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" JustifyContent="JustifyContent.End" Gap="0.5rem">
        @if (ComponentName == "Dialog" || ComponentName == "Tooltip" || ComponentName == "Notification" || ComponentName == "ContextMenu")
        {
            <a target="blank" class="rz-button rz-button-xs rz-variant-text rz-base rz-shade-default rz-text-truncate rz-text-capitalize" href=@($"https://github.com/radzenhq/radzen-blazor/blob/master/Radzen.Blazor/Radzen{ComponentName}.razor") title=@($"View Radzen{ComponentName} component source code in new tab") >
                <RadzenIcon Icon="open_in_new" /> @($"Radzen{ComponentName}") Source
            </a>
            <a target="blank" class="rz-button rz-button-xs rz-variant-text rz-base rz-shade-default rz-text-truncate rz-text-capitalize" href=@($"https://github.com/radzenhq/radzen-blazor/blob/master/Radzen.Blazor/{ComponentName}Service.cs") title=@($"View {ComponentName}Service source code in new tab") >
                <RadzenIcon Icon="open_in_new" /> @($"{ComponentName}Service") Source
            </a>
        }

        else {
            <a target="blank" class="rz-button rz-button-xs rz-variant-text rz-base rz-shade-default rz-text-truncate rz-text-capitalize" href="@ComponentSourceHref" title=@($"View Radzen{ComponentName} component source code in new tab") >
                <RadzenIcon Icon="open_in_new" /> @($"Radzen{ComponentName}") Source
            </a>
        }
        </RadzenStack>
    }
    </RadzenColumn>
</RadzenRow>
<RadzenAlert Visible=@(error != null) AlertStyle="AlertStyle.Danger" Variant="Variant.Flat" Shade="Shade.Lighter">@error</RadzenAlert>
<Monaco Value=@source Language="@language" ValueChanged=@OnValueChanged @ref="@monaco" Style="border: var(--rz-border-disabled)" Disabled=@isBusy ReadOnly=@ReadOnly />
@code {
    private string Copy => $"copy('{monaco?.Id}')";
    private Monaco monaco;
    private string source;
    private string error;
    private string language = "razor";
    private ICompilerService compiler;

    [Parameter]
    public EventCallback<Type> Compiled { get; set; }

    [Parameter]
    public string Value { get; set; }

    [Parameter]
    public EventCallback<string> ValueChanged { get; set; }

    [Parameter]
    public string PageName { get; set; }

    [Parameter]
    public bool ReadOnly { get; set; }

    /* Source */
    [Parameter]
    public string ComponentSource { get; set; }

    [Parameter]
    public string ComponentName { get; set; }

    string ComponentSourceHref => ComponentSource ?? $"https://github.com/radzenhq/radzen-blazor/blob/master/Radzen.Blazor/Radzen{ComponentName}.razor.cs";

    string PageSourceHref => Path.ChangeExtension(PageName != null && PageName.StartsWith("/") ? PageName : $"/demos/Pages/{PageName}", ".txt");
    protected override async Task OnParametersSetAsync()
    {
        language = PageName == null ? "razor" :  PageName.EndsWith(".razor") ? "razor" : "csharp";

        try
        {
            error = null;
            source = Value ?? await Fetch();
            source = Clean(source);
        }
        catch (Exception ex)
        {
            error = $"<p>Cannot fetch the example source code: <code>{ex.Message}</code></p><p>Make sure your browser has access to <a href=\"{PageSourceHref}\" target=\"_blank\">{PageSourceHref}</a></p>";
        }
    }

    async Task<string> Fetch()
    {
        return await Http.GetStringAsync(PageSourceHref);
    }

    string Clean(string source)
    {
        return Regex.Replace(source, "</?RadzenExample[^>]*>\n", "");
    }

    async Task OnValueChanged(string value)
    {
        source = value;

        await ValueChanged.InvokeAsync(value);
    }

    async Task Reset()
    {
        var source = await Fetch();

        source = Clean(source);

        await OnValueChanged(source);
    }

    bool isBusy;

    async Task Run()
    {
        try
        {
            error = null;
            isBusy = true;
            await JSRuntime.InvokeVoidAsync("eval", $"instances['{monaco.Id}'].updateOptions({{domReadOnly: true, readOnly: true }})");

            await Task.Yield();

            await EnsureCompilerAsync();

            var type = await compiler.CompileAsync(source);

            await Compiled.InvokeAsync(type);

        }
        catch (Exception ex)
        {
            error = ex.Message;
        }

        isBusy = false;
        await JSRuntime.InvokeVoidAsync("eval", $"instances['{monaco.Id}'].updateOptions({{domReadOnly: false, readOnly: false }})");
    }

    async Task EnsureCompilerAsync()
    {
        if (compiler != null)
        {
            return;
        }

        var assemblyLoader = ServiceProvider.GetService<LazyAssemblyLoader>();

        if (assemblyLoader != null)
        {
            await assemblyLoader.LoadAssembliesAsync(new[]
            {
                "Microsoft.CodeAnalysis.dll",
                "Microsoft.CodeAnalysis.CSharp.dll",
                "Microsoft.CodeAnalysis.Razor.dll",
                "Microsoft.AspNetCore.Razor.Language.dll",
                "Microsoft.AspNetCore.Mvc.Razor.Extensions.dll",
                "MetadataReferenceService.BlazorWasm.dll",
                "MetadataReferenceService.Abstractions.dll"
            });
        }

        var compilerType = AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetType("RadzenBlazorDemos.CompilerService", false)).FirstOrDefault(t => t != null);
        compiler = (ICompilerService)Activator.CreateInstance(compilerType, NavigationManager);
    }
}
